package com.fasterxml.jackson.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Objects;

/**
 * Annotation used for configuring details of if and how type information is
 * used with JSON serialization and deserialization, to preserve information
 * about actual class of Object instances. This is necessarily for polymorphic
 * types, and may also be needed to link abstract declared types and matching
 * concrete implementation.
 *<p>
 * Some examples of typical annotations:
 *<pre>
 *  // Include Java class name ("com.myempl.ImplClass") as JSON property "class"
 *  &#064;JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="class")
 *
 *  // Include logical type name (defined in impl classes) as wrapper; 2 annotations
 *  &#064;JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
 *  &#064;JsonSubTypes({com.myemp.Impl1.class, com.myempl.Impl2.class})
 *</pre>
 * Alternatively you can also define fully customized type handling by using
 * <code>&#064;JsonTypeResolver</code> annotation (from databind package).
 *<p>
 * This annotation can be used both for types (classes) and properties.
 * If both exist, annotation on property has precedence, as it is
 * considered more specific.
 *<p>
 * When used for properties (fields, methods), this annotation applies
 * to <b>values</b>: so when applied to structure types
 * (like {@link java.util.Collection}, {@link java.util.Map}, arrays),
 * will apply to contained values, not the container;
 * for non-structured types there is no difference.
 * This is identical to how JAXB handles type information
 * annotations; and is chosen since it is the dominant use case.
 * There is no per-property way to force type information to be included
 * for type of container (structured type); for container types one has
 * to use annotation for type declaration.
 *<p>
 * Note on visibility of type identifier: by default, deserialization
 * (use during reading of JSON) of type identifier
 * is completely handled by Jackson, and is <b>not passed to</b>
 * deserializers. However, if so desired,
 * it is possible to define property <code>visible = true</code>
 * in which case property will be passed as-is to deserializers
 * (and set via setter or field) on deserialization.
 *<p>
 * On serialization side, Jackson will generate type id by itself,
 * except if there is a property with name that matches
 * {@link #property()}, in which case value of that property is
 * used instead.
 *<p>
 * NOTE: use of type id of "class name" with very general base type
 * (such as {@link java.lang.Object} or {@link java.io.Serializable})
 * can potentially open up security holes if deserializing content
 * generated by untrusted sources. If content can not be trusted,
 * it is necessary to either use "type name" as type id, or to
 * limit possible types using other means.
 */
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE,
    ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonTypeInfo
{
    /*
    /**********************************************************
    /* Value enumerations used for properties
    /**********************************************************
     */

    /**
     * Definition of different type identifiers that can be included in JSON
     * during serialization, and used for deserialization.
     */
    public enum Id {
        /**
         * This means that no explicit type metadata is included, and typing is
         * purely done using contextual information possibly augmented with other
         * annotations. Or, format-specific metadata mechanism is used.
         * Typically used with {@link As#NOTHING}.
         */
        NONE(null),

        /**
         * Means that fully-qualified Java class name is used as the type identifier.
         */
        CLASS("@class"),

        /**
         * Means that Java class name with minimal path is used as the type identifier.
         * Minimal means that only the class name, and that part of preceding Java
         * package name is included that is needed to construct fully-qualified name
         * given fully-qualified name of the declared supertype; additionally a single
         * leading dot ('.') must be used to indicate that partial class name is used.
         * For example, for supertype {@code com.foo.Base}, and concrete type
         * {@code com.foo.Impl}, only {@code .Impl} would be included;
         * and for {@code com.foo.impl.Impl2}  only {@code .impl.Impl2} would be included.
         *<br>
         * <b>NOTE</b>: leading dot ('.') MUST be used to denote partial (minimal) name;
         * if it is missing, value is assumed to be fully-qualified name. Fully-qualified
         * name is used in cases where subtypes are not in same package (or sub-package
         * thereof) as base class.
         *<p>
         * If all related classes are in the same Java package, this option can reduce
         * amount of type information overhead, especially for small types.
         * However, please note that using this alternative is inherently risky since it
         * assumes that the
         * supertype can be reliably detected. Given that it is based on declared type
         * (since ultimate supertype, <code>java.lang.Object</code> would not be very
         * useful reference point), this may not always work as expected.
         */
        MINIMAL_CLASS("@c"),

        /**
         * Means that logical type name is used as type information; name will then need
         * to be separately resolved to actual concrete type (Class).
         */
        NAME("@type"),

        /**
         * Means that the simple name of the Java class, equivalent to the value returned by {@link Class#getSimpleName()},
         * is used as the default type identifier, unless explicit name is specified by annotation {@link JsonTypeName}.
         *<br>
         * For instance: 
         * <ul>
         *     <li>For a class "com.example.MyClass", only "MyClass" is used.</li>
         *     <li>For an inner class "com.example.MyClass$Inner", only "Inner" is used.</li>
         * </ul>
         * <b>Note:</b> This approach reduces verbosity but requires the simple names to be unique 
         * to avoid conflicts. If multiple classes share the same simple name, <b>the last declared one</b>
         * will be used. This approach should be used with careful consideration of your type hierarchy.
         *
         * @since 2.16
         */
        SIMPLE_NAME("@type"),

        /**
         * Means that no serialized typing-property is used. Types are <i>deduced</i> based
         * on the Object properties available in the input.
         * Deduction is limited to the <i>names</i> of properties
         * (not their values or, consequently, any nested values). Exceptions will be
         * thrown if not enough unique information is present to select a single sub-type.
         * <br>If deduction is being used annotation properties {@code visible},
         * {@code property} and {@code include} are ignored.
         * <p>
         * NOTE: being Property-based, will essentially only work for POJOs and not for
         * other kinds of types (like arrays, {@link java.util.Collection}s, {@link java.util.Map}s,
         * scalars, etc); similarly, serialized form must be (JSON) Object.
         * <p>
         * On serialization, no type Id is written, and only regular properties are included.
         *
         * @since 2.12
         */
        DEDUCTION(null),

        /**
         * Means that typing mechanism uses customized handling, with possibly
         * custom configuration. This means that semantics of other properties is
         * not defined by Jackson package, but by the custom implementation.
         */
        CUSTOM(null)
        ;

        private final String _defaultPropertyName;

        private Id(String defProp) {
            _defaultPropertyName = defProp;
        }

        public String getDefaultPropertyName() { return _defaultPropertyName; }
    }

    /**
     * Definition of standard type inclusion mechanisms for type metadata.
     * Used for standard metadata types.
     * May or may not be used for custom types ({@link Id#CUSTOM}).
     */
    public enum As {
        /**
         * Inclusion mechanism that uses a single configurable property, included
         * along with actual data (POJO properties) as a separate meta-property.
         * <p>
         * Default choice for inclusion.
         */
        PROPERTY,

        /**
         * Inclusion mechanism that wraps typed JSON value (POJO
         * serialized as JSON) in
         * a JSON Object that has a single entry,
         * where field name is serialized type identifier,
         * and value is the actual JSON value.
         *<p>
         * Note: can only be used if type information can be serialized as
         * String. This is true for standard type metadata types, but not
         * necessarily for custom types.
         */
        WRAPPER_OBJECT,

        /**
         * Inclusion mechanism that wraps typed JSON value (POJO
         * serialized as JSON) in
         * a 2-element JSON array: first element is the serialized
         * type identifier, and second element the serialized POJO
         * as JSON Object.
         */
        WRAPPER_ARRAY,

        /**
         * Inclusion mechanism similar to <code>PROPERTY</code>, except that
         * property is included one-level higher in hierarchy, i.e. as sibling
         * property at same level as JSON Object to type.
         * Note that this mechanism <b>can only be used for properties</b>, not
         * for types (classes). Trying to use it for classes will result in
         * inclusion strategy of basic <code>PROPERTY</code> instead.
         *<p>
         * Note also that this mechanism <b>can not be used for container values</b>
         * (arrays, {@link java.util.Collection}s, {@link java.util.Map}s); it only
         * works for scalar and POJO values.
         */
        EXTERNAL_PROPERTY,

        /**
         * Inclusion mechanism similar to <code>PROPERTY</code> with respect
         * to deserialization; but one that is produced by a "regular" accessible
         * property during serialization. This means that <code>TypeSerializer</code>
         * will do nothing, and expects a property with defined name to be output
         * using some other mechanism (like default POJO property serialization, or
         * custom serializer).
         *<p>
         * Note that this behavior is quite similar to that of using {@link JsonTypeId}
         * annotation;
         * except that here <code>TypeSerializer</code> is basically suppressed;
         * whereas with {@link JsonTypeId}, output of regular property is suppressed.
         * This mostly matters with respect to output order; this choice is the only
         * way to ensure specific placement of type id during serialization.
         *
         * @since 2.3 but databind <b>only since 2.5</b>.
         */
        EXISTING_PROPERTY,

        /**
         * Pseudo-value used as marker for case where no in-data Type Id is included:
         * either because separate metadata (using format native mechanism) is used,
         * or inference mechanism is used or possibly no Type Id is used at all
         * (to match {@link Id#NONE} case).
         *
         * @since 2.21
         */
        NOTHING
        ;
    }

    /*
    /**********************************************************
    /* Annotation properties
    /**********************************************************
     */

    /**
     * Specifies kind of type metadata to use when serializing
     * type information for instances of annotated type
     * and its subtypes; as well as what is expected during
     * deserialization.
     */
    public Id use();

    /**
     * Specifies mechanism to use for including type metadata (if any; for
     * {@link Id#NONE} nothing is included); used when serializing,
     * and expected when deserializing.
     *<p>
     * Note that for type metadata type of {@link Id#CUSTOM},
     * this setting may or may not have any effect.
     */
    public As include() default As.PROPERTY;

    /**
     * Property names used when type inclusion method ({@link As#PROPERTY}) is used
     * (or possibly when using type metadata of type {@link Id#CUSTOM}).
     * If POJO itself has a property with same name, value of property
     * will be set with type id metadata: if no such property exists, type id
     * is only used for determining actual type.
     *<p>
     * Default property name used if this property is not explicitly defined
     * (or is set to empty <code>String</code>) is based on
     * type metadata type ({@link #use}) used.
     */
    public String property() default "";

    /**
     * Optional property that can be used to specify default implementation
     * class to use for deserialization if type identifier is either not present,
     * or can not be mapped to a registered type (which can occur for ids,
     * but not when specifying explicit class to use).
     * Property has no effect on choice of type id used for serialization;
     * it is only used in deciding what to do for otherwise unmappable cases.
     *<p>
     * Note that while this property allows specification of the default
     * implementation to use, it does not help with structural issues that
     * may arise if type information is missing. This means that most often
     * this is used with type-name -based resolution, to cover cases
     * where new subtypes are added, but base type is not changed to
     * reference new subtypes.
     *<p>
     * There are certain special values that indicate alternate behavior:
     *<ul>
     * <li>{@link java.lang.Void} means that objects with unmappable (or missing)
     *    type are to be mapped to null references.
     *    For backwards compatibility (2.5 and below), value of
     *    <code>com.fasterxml.jackson.databind.annotation.NoClass</code> is also allowed
     *    for such usage.
     *  </li>
     * <li>Placeholder value of {@link JsonTypeInfo} (that is, this annotation type
     *    itself} means "there is no default implementation" (in which
     *   case an error results from unmappable type).
     *   For backwards compatibility with earlier versions (2.5 and below),
     *   value of {@link JsonTypeInfo.None} may also be used.
     *  </li>
     * </ul>
     */
    public Class<?> defaultImpl() default JsonTypeInfo.class;

    /**
     * Property that defines whether type identifier value will be passed
     * as part of JSON stream to deserializer (true), or handled and
     * removed by <code>TypeDeserializer</code> (false).
     * Property has no effect on serialization.
     *<p>
     * Default value is false, meaning that Jackson handles and removes
     * the type identifier from JSON content that is passed to
     * <code>JsonDeserializer</code>.
     *
     * @since 2.0
     */
    public boolean visible() default false;

    /*
    /**********************************************************
    /* Helper classes
    /**********************************************************
     */

    /**
     * This marker class that is only to be used with <code>defaultImpl</code>
     * annotation property, to indicate that there is no default implementation
     * specified.
     *
     * @deprecated Since 2.5, use any Annotation type (such as {@link JsonTypeInfo}),
     *    if such behavior is needed; this is rarely necessary.
     */
    @Deprecated // since 2.5
    public abstract static class None {}

    /**
     * Specifies whether the type ID should be strictly required during polymorphic
     * deserialization of its subtypes.
     * <p>
     * If set to {@link OptBoolean#TRUE}, an {@code InvalidTypeIdException} will
     * be thrown if no type information is provided. 
     * If set to {@link OptBoolean#FALSE}, deserialization may proceed without
     * type information if the  subtype is a legitimate target (non-abstract). 
     * If set to {@link OptBoolean#DEFAULT}, the global configuration of 
     * {@code MapperFeature#REQUIRE_TYPE_ID_FOR_SUBTYPES} is used for type ID handling.
     * <p>
     * NOTE: This setting is specific to this type and will <strong>always override</strong>
     * the global configuration of {@code MapperFeature.REQUIRE_TYPE_ID_FOR_SUBTYPES}.
     *
     * @since 2.16
     */
    public OptBoolean requireTypeIdForSubtypes() default OptBoolean.DEFAULT;

    /*
    /**********************************************************************
    /* Value class used to enclose information, allow for
    /* merging of layered configuration settings.
    /**********************************************************************
     */

    public static class Value
        implements JacksonAnnotationValue<JsonTypeInfo>,
            java.io.Serializable
    {
        private static final long serialVersionUID = 1L;

        // should not really be needed usually but make sure defaults to `NONE`; other
        // values of less interest
        protected final static Value EMPTY = new Value(Id.NONE, As.NOTHING, null, null, false, null);

        protected final Id _idType;
        protected final As _inclusionType;
        protected final String _propertyName;

        protected final Class<?> _defaultImpl;
        protected final boolean _idVisible;
        protected final Boolean _requireTypeIdForSubtypes;

        /*
        /**********************************************************************
        /* Construction
        /**********************************************************************
         */

        protected Value(Id idType, As inclusionType,
                String propertyName, Class<?> defaultImpl, boolean idVisible, Boolean requireTypeIdForSubtypes)
        {
            _defaultImpl = defaultImpl;
            _idType = idType;
            _inclusionType = inclusionType;
            _propertyName = propertyName;
            _idVisible = idVisible;
            _requireTypeIdForSubtypes = requireTypeIdForSubtypes;
        }

        public static Value construct(Id idType, As inclusionType,
                String propertyName, Class<?> defaultImpl, boolean idVisible, Boolean requireTypeIdForSubtypes)
        {
            // couple of overrides we need to apply here. First: if no propertyName specified,
            // use Id-specific property name
            if ((propertyName == null) || propertyName.isEmpty()) {
                if (idType != null) {
                    propertyName = idType.getDefaultPropertyName();
                } else {
                    propertyName = "";
                }
            }
            // Although we can not do much here for special handling of `Void`, we can convert
            // annotation types as `null` (== no default implementation)
            if ((defaultImpl == null) || defaultImpl.isAnnotation()) {
                defaultImpl = null;
            }
            return new Value(idType, inclusionType, propertyName, defaultImpl, idVisible, requireTypeIdForSubtypes);
        }

        public static Value from(JsonTypeInfo src) {
            if (src == null) {
                return null;
            }
            return construct(src.use(), src.include(),
                    src.property(), src.defaultImpl(), src.visible(), src.requireTypeIdForSubtypes().asBoolean());
        }

        /*
        /**********************************************************************
        /* Mutators
        /**********************************************************************
         */

        public Value withDefaultImpl(Class<?> impl) {
            return (impl == _defaultImpl) ? this :
                new Value(_idType, _inclusionType, _propertyName, impl, _idVisible, _requireTypeIdForSubtypes);
        }

        public Value withIdType(Id idType) {
            return (idType == _idType) ? this :
                new Value(idType, _inclusionType, _propertyName, _defaultImpl, _idVisible, _requireTypeIdForSubtypes);
        }

        public Value withInclusionType(As inclusionType) {
            return (inclusionType == _inclusionType) ? this :
                new Value(_idType, inclusionType, _propertyName, _defaultImpl, _idVisible, _requireTypeIdForSubtypes);
        }

        public Value withPropertyName(String propName) {
            return (propName == _propertyName) ? this :
                new Value(_idType, _inclusionType, propName, _defaultImpl, _idVisible, _requireTypeIdForSubtypes);
        }

        public Value withIdVisible(boolean visible) {
            return (visible == _idVisible) ? this :
                new Value(_idType, _inclusionType, _propertyName, _defaultImpl, visible, _requireTypeIdForSubtypes);
        }
        
        public Value withRequireTypeIdForSubtypes(Boolean requireTypeIdForSubtypes) {
            return (_requireTypeIdForSubtypes == requireTypeIdForSubtypes) ? this :
                new Value(_idType, _inclusionType, _propertyName, _defaultImpl, _idVisible, requireTypeIdForSubtypes);
        }

        /*
        /**********************************************************************
        /* Simple accessors
        /**********************************************************************
         */

        @Override
        public Class<JsonTypeInfo> valueFor() {
            return JsonTypeInfo.class;
        }

        public Class<?> getDefaultImpl() { return _defaultImpl; }
        public Id getIdType() { return _idType; }
        public As getInclusionType() { return _inclusionType; }
        public String getPropertyName() { return _propertyName; }
        public boolean getIdVisible() { return _idVisible; }
        public Boolean getRequireTypeIdForSubtypes() { return _requireTypeIdForSubtypes; }

        /**
         * Static helper method for simple(r) checking of whether there's a Value instance
         * that indicates that polymorphic handling is (to be) enabled.
         */
        public static boolean isEnabled(JsonTypeInfo.Value v) {
            return (v != null) &&
                (v._idType != null) && (v._idType != Id.NONE)
                && (v._inclusionType != As.NOTHING);
        }

        /*
        /**********************************************************************
        /* Standard methods
        /**********************************************************************
         */

        @Override
        public String toString() {
            return String.format("JsonTypeInfo.Value(idType=%s,includeAs=%s,propertyName=%s,defaultImpl=%s,idVisible=%s" 
                            + ",requireTypeIdForSubtypes=%s)",
                    _idType, _inclusionType, _propertyName,
                    ((_defaultImpl == null) ? "NULL" : _defaultImpl.getName()),
                    _idVisible, _requireTypeIdForSubtypes);
        }

        @Override
        public int hashCode() {
            int hashCode = 1;
            hashCode = 31 * hashCode + (_idType != null ? _idType.hashCode() : 0);
            hashCode = 31 * hashCode + (_inclusionType != null ? _inclusionType.hashCode() : 0);
            hashCode = 31 * hashCode + (_propertyName != null ? _propertyName.hashCode() : 0);
            hashCode = 31 * hashCode + (_defaultImpl != null ? _defaultImpl.hashCode() : 0);
            hashCode = 31 * hashCode + (_requireTypeIdForSubtypes ? 11 : -17);
            hashCode = 31 * hashCode + (_idVisible ? 11 : -17);
            return hashCode;
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o == null) return false;
            return (o.getClass() == getClass())
                    && _equals(this, (Value) o);
        }

        private static boolean _equals(Value a, Value b)
        {
            return (a._idType == b._idType)
                    && (a._inclusionType == b._inclusionType)
                    && (a._defaultImpl == b._defaultImpl)
                    && (a._idVisible == b._idVisible)
                    && Objects.equals(a._propertyName, b._propertyName)
                    && Objects.equals(a._requireTypeIdForSubtypes, b._requireTypeIdForSubtypes)
            ;
        }
    }
}
